package com.googlecode.sere.translation

case class MediaRange(
  mainType: Option[String],
  subType: Option[String],
  params: Map[String, String] = Map()) {
  
  // Class invariant

  private val invalid = "Invalid media range"

  if (mainType.isEmpty && !subType.isEmpty)
    throw new IllegalArgumentException("%s: if type is wildcard, subType should be too" format (invalid))

  if (subType.isEmpty && !params.isEmpty)
    throw new IllegalArgumentException("%s: wildcard ranges cannot have parameters" format (invalid))

  if (params.contains("q"))
    throw new IllegalArgumentException("%s: \"q\" is not allowed as a param" format (invalid))

  // TODO: Check for valid chars

  def matchType(mediaType: MediaType): Int = {
    mainType match {
      case None => return 1
      case Some(t) if (t != mediaType.mainType) => return 0
      case _ => // Go on
    }
    assert(mediaType.mainType == mainType.get)
    subType match {
      case None => return 2
      case Some(t) if (t != mediaType.subType) => return 0
      case _ => // Go on
    }
    assert(mediaType.subType == subType.get)

    // No params means wildcard params.
    // See: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1
    if (params.isEmpty)
      return 3

    if (mediaType.params == params)
      return 4
    else
      return 0
  }

  override def toString: String = {
    def toStr(s: Option[String]) = s.getOrElse("*")
    val sb = new StringBuilder
    sb.append("%s/%s" format (toStr(mainType), toStr(subType)))
    for ((name, value) <- params) {
      sb.append(";%s=%s" format (name, value))
    }
    sb.toString
  }
}

object MediaRange {
    
  def apply(mainType: String, subType: String, params: Map[String, String]) =
    new MediaRange(Some(mainType), Some(subType), params)

  def apply(mainType: String, subType: String): MediaRange =
    apply(mainType, subType, Map[String, String]())

  def parse(string: String) = {
    val regex = "(.+?)/(.+)".r
    string match {
      case regex("*", "*") => (None, None)
      case regex(mainType, "*") => (Some(mainType), None)
      case regex(mainType, subType) => (Some(mainType), Some(subType))
      case s => throw new Exception("Invalid media range string: " + s)
    }
  }
}
